#include "Const.h"
#include "Snake.h"

/**
 * 构造方法。
 * @param bitmap    台缓冲位图
 */
Snake::Snake(QPixmap* bitmap) :
    mDirection(Qt::Key_Down) {
    QPoint caudaPos(0, 0);
    mHead = mCauda = new SnakeBody(bitmap, &caudaPos, SnakeBody::SNAKE);
}

/**
 * 析构方法。
 */
Snake::~Snake() {
    // 从蛇尾开始，删除所有蛇身
    SnakeBody* snakeBody = mCauda->getNext();
    while (snakeBody != nullptr) {
        delete mCauda;
        mCauda = snakeBody;
        snakeBody = snakeBody->getNext();
    }

    // 刪除蛇头
    delete mHead;
}

/**
 * 吃果子，即将果子放到蛇尾。
 * @param apple 果子
 */
void Snake::eat(SnakeBody* apple) {
    // 设置吃到的果子的位置
    QPoint position(*mHead->getPosition());
    switch (mDirection) {
        case Qt::Key_Down:
            position.setY(position.y() + 1);
            break;
        case Qt::Key_Left:
            position.setX(position.x() - 1);
            break;
        case Qt::Key_Right:
            position.setX(position.x() + 1);
            break;
        case Qt::Key_Up:
            position.setY(position.y() - 1);
            break;
    }
    apple->setPosition(&position);

    // 改变果子的类型
    apple->setType(SnakeBody::SNAKE);

    // 重绘蛇头和果子
    mHead->draw();
    apple->draw();

    // 将吃到的果子变成蛇头
    mHead->setNext(apple);
    mHead = apple;
}

/**
 * 获取蛇头。
 * @return 蛇头
 */
SnakeBody* Snake::getHead() {
    return mHead;
}

/**
 * 移动。
 * @return 蛇头碰到自身或游戏区域边界则返回 true，否则返回 false
 */
bool Snake::move() {
    // 计算蛇尾的新位置并抹除蛇尾
    QPoint point(*mHead->getPosition());
    switch (mDirection) {
        case Qt::Key_Down:
            if (point.y() + 1 >= Const::GAME_BOARD_GRID_COUNT_V) {
                // 如果蛇头超出游戏区域下边界，则返回
                return true;
            }
            mCauda->erase();
            point.setY(point.y() + 1);
            break;
        case Qt::Key_Left:
            if (point.x() - 1 < 0) {
                // 如果蛇头超出游戏区域左边界，则返回
                return true;
            }
            mCauda->erase();
            point.setX(point.x() - 1);
            break;
        case Qt::Key_Right:
            if (point.x() + 1 >= Const::GAME_BOARD_GRID_COUNT_H) {
                // 如果蛇头超出游戏区域右边界，则返回
                return true;
            }
            mCauda->erase();
            point.setX(point.x() + 1);
            break;
        case Qt::Key_Up:
            if (point.y() - 1 < 0) {
                // 如果蛇头超出游戏区域上边界，则返回
                return true;
            }
            mCauda->erase();
            point.setY(point.y() - 1);
            break;
    }
    mCauda->setPosition(&point);

    // 将蛇尾的下一个结点设置为蛇尾、原蛇尾设置成蛇头
    mHead->setNext(mCauda);
    mHead = mCauda;
    mCauda = mCauda->getNext();
    mHead->setNext(nullptr);
    mHead->draw();

    // 检查蛇头是否碰到蛇身
    for (SnakeBody* snakeBody = mCauda; snakeBody != mHead; snakeBody = snakeBody->getNext()) {
        if (snakeBody->getPosition() == mHead->getPosition()) {
            return true;
        }
    }

    return false;
}

/**
 * 检查某点是否与蛇身位置重叠。
 * @param point 被检查的点
 * @return 点与蛇身位置重叠则返回 true ，否则返回 false
 */
bool Snake::pointIsOnMe(QPoint* point) {
    for (SnakeBody* snakeBody = mCauda; snakeBody != nullptr; snakeBody = snakeBody->getNext()) {
        if (*snakeBody->getPosition() == *point) {
            return true;
        }
    }
    return false;
}

/**
 * 转弯。
 * @param direction 方向
 */
void Snake::turn(int direction) {
    // 如果方向与当前方向相反（想要直接调头），则直接返回
    if (abs(direction - mDirection) == 2) {
        return;
    }
    mDirection = direction;
}
